/**
* @class: FB_Persona_Client
* @author: Alexei Bratuhin
* @description: connects to Facebook profile using a java-library from Facebook,
		requests data about all co-groupers,
		filters those, whose profile can be viewed by applications owner,
		selects 10 most promising profiles according to given tags,
		opens 3 of them in browser.
**/

package org.abratuhi.fbpersona.src.model;

import java.awt.Component;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;

import javax.swing.JOptionPane;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import com.facebook.api.FacebookException;
import com.facebook.api.FacebookXmlRestClient;

import edu.stanford.ejalbert.BrowserLauncher;
import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;

/**
* Constructor
**/
public class FB_Persona_Client {
	
	// CONFIG file must contain (api_key, secret) given by FAcebook to this application (desktop=true/false) in java-properties format
	public static String CONFIG = "fbpersona.properties";
	
	//tag lists, that are considered typical for each of three "it expirience" groups: novice/medium/expert
	//may by easily enlarged, since the list is being weighted after comparison with an user's profile
	public static String[] exp_novice = {};
	public static String[] exp_medium = {"game", "blog", "web","world","craft","computer","pc",
										"virtual", "internet", "second life"};
	public static String[] exp_expert = {"it", "software", "java", "perl", "php",
										"ruby", "c++", "ada", "fortran", "haskell",
										"pascal", "basic", ".net", "sql", "nix", "source", 
										"programm", "tcp", "computer science"};
	//declaration of Facebook client
	FacebookXmlRestClient c;
	//declaration of owner-specific data 
	String myid = new String(); //owner's facebook id
	ArrayList<String> mynid = new ArrayList<String>(); //list of owner's network id's
	
	public FB_Persona_Client(){
		
	}
	
	public void login(Component co){
	    try {
			//load facebookapplication-specific data from (defined by user) javaproperties file
			FileInputStream fis = new FileInputStream(CONFIG);
		    Properties props = new Properties();
			props.load(fis);
			
			//initailize a facebook client with given properties
		    c = new FacebookXmlRestClient(props.getProperty("facebook.api_key"), props.getProperty("facebook.secret"));
		    if (null != props.getProperty("facebook.desktop") && !"0".equals(props.getProperty("facebook.desktop"))) c.setIsDesktop(true);
		    System.out.println("api_key: " + props.getProperty("facebook.api_key")+ "; secret: "+ props.getProperty("facebook.secret"));
		    
			//begin user's authorization in browser window
		    String auth = c.auth_createToken();
		    System.out.println("auth token: " + auth);
		    BrowserLauncher bl = new BrowserLauncher(null);
		    bl.openURLinBrowser(props.getProperty("facebook.login_url") + "&api_key=" + props.getProperty("facebook.api_key") + "&auth_token=" + auth);
		    JOptionPane.showMessageDialog(co, "Press OK when logged in in browser", "Message", JOptionPane.WARNING_MESSAGE);
		    c.auth_getSession(auth);
			//from now on, thw user (owner of the application) is logged in and can request facebook data
		} catch (IOException e) {
			e.printStackTrace();
		} catch (FacebookException e) {
			e.printStackTrace();
		} catch (BrowserLaunchingInitializingException e) {
			e.printStackTrace();
		} catch (UnsupportedOperatingSystemException e) {
			e.printStackTrace();
		} catch (BrowserLaunchingExecutionException e) {
			e.printStackTrace();
		}
	}
	
	public void logout(){
		
	}
	
	/**
	* simple function, implemented for place saving and better code readability
	* @q - input FQL query
	* @return XML document sent as response to @q by Facebook
	**/
	public Document query(String q){
		try {
			String auth = c.auth_createToken();
		    c.auth_getSession(auth);
		    Document doc = c.fql_query(q);
			return doc;
		} catch (FacebookException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	/**
	* this function contains the main functonality of the application. It selects users with the same nid as the owner's, what means (per default facebook configuration, which is rarely changed),
	* that their profiles can be viewed by owner; and then partially arranges them in an ordered list, built upon considering weighted number of tags occured in profile
	**/
	public String search(String area, String exp, String[] tags){
		// get myid, mynid
		getAboutMe();
		// hence facebook hasn't yet made nid indexable, one has to use a simple trick:
		// first, retrieve all possible uid via common group memberships
		String q = "select uid,affiliations.nid " +
					"from user " +
					"where uid in (select uid from group_member where gid in (select gid from group_member where uid="+myid+"))";
		Document d = query(q);
		
		// second, compare the nid of uid one got in previous section to owners nids
		// select those uids, which have the same nid as the owner
		ArrayList<String> users = new ArrayList<String>();
		NodeList nl = d.getElementsByTagName("nid");
		for(int i=0; i<nl.getLength(); i++){
			String curnid = nl.item(i).getTextContent();
			if(mynid.contains(curnid) && !users.contains(curnid)){
				users.add(nl.item(i).getParentNode().getParentNode().getParentNode().getFirstChild().getTextContent());
			}
		}
		
		// now query for information - but only those users, that have the same nid as the owner's
		q = "";
		q +="select uid,about_me,activities,hs_info,interests,notes_count,political,relationship_status,religion,sex,wall_count,work_history.location.country,work_history.position,work_history.description " +
				"from user ";
		q += "where uid in (";
		for(int i=0; i<users.size()-1; i++){
			q += users.get(i)+",";
		}
		q += users.get(users.size()-1);
		q += ")";
		System.out.println(q);
		d = query(q);
		
		// build one-string profiles
		users.clear();
		ArrayList<String> userprofiles = new ArrayList<String>();
		for(int i=0; i<d.getElementsByTagName("user").getLength(); i++){
			users.add(d.getElementsByTagName("user").item(i).getFirstChild().getTextContent());
			userprofiles.add(d.getElementsByTagName("user").item(i).getTextContent());
		}
		int nuser = userprofiles.size();
		double[] jtf_1 = new double[nuser];
		double[] jtf_2 = new double[nuser];
		double[] jtf_3 = new double[nuser];
		
		// check profile-strings, whether they contain the desired tag combination and weight the occurence
		for(int i=0; i<nuser; i++){
			//1
			jtf_1[i] = 0.0;
			if(userprofiles.get(i).contains(area)) {jtf_1[i] += 1.0 / 1.0;/*System.out.println(usable.get(i)+" contains "+area);*/}
			//2
			jtf_2[i] = 0;
			if(exp.equals("novice")) jtf_2[i] += 1.0 / 1.0;
			if(exp.equals("medium")){
				for(int j=0; j<exp_medium.length; j++){
					if(userprofiles.get(i).contains(exp_medium[j])) {jtf_2[i] += 1.0 / exp_medium.length;/*System.out.println(usable.get(i)+" contains "+exp_medium[j]);*/} 
				}
			}
			if(exp.equals("expert")){
				for(int j=0; j<exp_expert.length; j++){
					if(userprofiles.get(i).contains(exp_expert[j])) {jtf_2[i] += 1.0 / exp_expert.length;/*System.out.println(usable.get(i)+" contains "+exp_expert[j]);*/} 
				}
			}
			//3
			for(int j=0; j<tags.length; j++){
				if(userprofiles.get(i).contains(tags[j])) {jtf_3[i] += 1.0 / tags.length;/*System.out.println(usable.get(i)+" contains "+tags[j]);*/}
			}
		}
		
		// sum the weights through all 3 fields
		double[][] res = new double[nuser][3];
		for(int i=0; i<nuser; i++){
			//res[i][0] = 0;
			res[i][0] = jtf_1[i] + jtf_2[i] + jtf_3[i];
			res[i][1] = Double.parseDouble(users.get(i));
			res[i][2] = i;
		}
		
		// sort out the 10 greatest values and select their uid
		int needed = 10;
		for(int i=0; i<needed; i++){
			int imax = -1;
			double tmax = 0;
			for(int j=i; j<nuser; j++){
				if(res[j][0]>tmax) {tmax = res[j][0]; imax = j;}
			}
			if(imax != -1) {double t1 = res[imax][0], t2 = res[imax][1], t3 = res[imax][2]; 
							res[imax][0] = res[i][0]; res[imax][1] = res[i][1]; res[imax][2]=res[i][2];
							res[i][0] = t1; res[i][1] = t2; res[i][2] = t3;}
		}

		for(int i=0; i<nuser; i++){
			System.out.println(users.get(i) + " : "+ userprofiles.get(i));
		}
		
		// set the final output string, that 'll be shown in GUI window
		String outuser = new String();
		for(int i=0; i<needed; i++){
			outuser += "http://www.facebook.com/profile.php?id=" + users.get((int) res[i][2])+"\n";
		}
		
		// open three possibly most desirable profiles in browser
		try {
			new BrowserLauncher(null).openURLinBrowser("http://www.facebook.com/profile.php?id=" + users.get((int) res[0][2])+"\n");
			new BrowserLauncher(null).openURLinBrowser("http://www.facebook.com/profile.php?id=" + users.get((int) res[1][2])+"\n");
			new BrowserLauncher(null).openURLinBrowser("http://www.facebook.com/profile.php?id=" + users.get((int) res[2][2])+"\n");
		} catch (BrowserLaunchingInitializingException e) {
			e.printStackTrace();
		} catch (UnsupportedOperatingSystemException e) {
			e.printStackTrace();
		} catch (BrowserLaunchingExecutionException e) {
			e.printStackTrace();
		}
		
		return outuser;
	}
	
	/**
	* function for setting myid and mynid values ->
	* get own facebook id together with all my network ids
	*/
	public void getAboutMe(){
		try {
			myid = String.valueOf(c.users_getLoggedInUser());
			Document d = query("select affiliations.nid from user where uid="+myid);
			NodeList nl = d.getElementsByTagName("nid");
			int size = nl.getLength();
			mynid.clear();
			for(int i=0; i<size; i++){
				mynid.add(nl.item(i).getTextContent());
			}
		} catch (FacebookException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}
